/** @file         lzm_notifier.c
 *  @brief        简要说明
 *  @details      详细说明
 *  @author       lzm
 *  @date         2021-09-01 12:10:06
 *  @version      v1.1
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *  @blog         https://www.cnblogs.com/lizhuming/
 *
 **********************************************************
 *  @LOG 修改日志: 
 *  v1.1 add lock
 **********************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lzm_notifier.h"

/**
 * @name   add_notifier
 * @brief  add a new function to be called when something happens.
 * @param  
 * @retval 
 * @author lzm
 */
int add_notifier(notifier_ctx_t *notifier_ctx_ptr, notify_func func, void *arg)
{
	if(notifier_ctx_ptr == NULL || func == NULL)
    	return -1;

    notifier_t *np = NULL;

  	pthread_mutex_lock(&notifier_ctx_ptr->lock);
	
    for(np = notifier_ctx_ptr->notif; np != NULL; np = np->next) 
    {
		if (np->func == func && np->arg == arg) 
      	{
			pthread_mutex_unlock(&notifier_ctx_ptr->lock);
		    return 0;		// already exist
		}
    }

    np = (notifier_t *)malloc(sizeof(notifier_t));
    if (np == NULL) 
    {
		pthread_mutex_unlock(&notifier_ctx_ptr->lock);
		printf("no mem\n");
		return -1;
	}

    np->next = notifier_ctx_ptr->notif;
    np->func = func;
    np->arg = arg;
    notifier_ctx_ptr->notif = np;

	pthread_mutex_unlock(&notifier_ctx_ptr->lock);

	return 0;
}


/**
 * @name   add_notifier
 * @brief  remove a function from the list of things to be called when something happens.
 * @param  
 * @retval 
 * @author lzm
 */
int remove_notifier(notifier_ctx_t *notifier_ctx_ptr,  notify_func func, void *arg)
{
	if(notifier_ctx_ptr == NULL || notifier_ctx_ptr->notif == NULL || func == NULL)
    	return -1;

    notifier_t *np = NULL;
	notifier_t *np_last = NULL;
  
  	pthread_mutex_lock(&notifier_ctx_ptr->lock);
	
	np_last = notifier_ctx_ptr->notif;
	np = notifier_ctx_ptr->notif;
	while(1)
	{
		if (np->func == func && np->arg == arg) 
		{
			if(np_last == np)
				notifier_ctx_ptr->notif =np->next;
			else
				np_last->next = np->next;
			
			free(np);
			break;
		}

		if(np->next == NULL)
			break;
		
		np_last = np;
		np = np->next;
	}

	pthread_mutex_unlock(&notifier_ctx_ptr->lock);
	return 0;
}

/**
 * @name   notify
 * @brief  call a set of functions registered with add_notify. (执行该单向链表中的所有回调函数)
 * @param  
 * @retval 
 * @author lzm
 */
int notify(notifier_ctx_t *notifier_ctx_ptr, int type, int val)
{
	if(notifier_ctx_ptr == NULL)
    	return -1;

    notifier_t *np = NULL;

  	pthread_mutex_lock(&notifier_ctx_ptr->lock);

    np = notifier_ctx_ptr->notif;
    while (np != NULL) 
    {
		(*np->func)(np->arg, type, val);
		np = np->next;
    }

	pthread_mutex_unlock(&notifier_ctx_ptr->lock);

	return 0;
}

/**
 * @name   notify_init
 * @brief  入口func
 * @param  
 * @retval 
 * @author lzm
 */
int notify_init(notifier_ctx_t *notifier_ctx_ptr)
{
	if(notifier_ctx_ptr != NULL)
		return -1;

	notifier_ctx_ptr = malloc(sizeof(notifier_ctx_t));
	if(notifier_ctx_ptr == NULL)
		return -2;

	memset(notifier_ctx_ptr, 0x00, sizeof(notifier_ctx_t));

	pthread_mutex_init(&notifier_ctx_ptr->lock, NULL);

  	pthread_mutex_lock(&notifier_ctx_ptr->lock);

	notifier_ctx_ptr->notif = NULL;
	
	pthread_mutex_unlock(&notifier_ctx_ptr->lock);

	return 0;
}

/**
 * @name   notify_exit
 * @brief  出口func
 * @param  
 * @retval 
 * @author lzm
 */
int notify_exit(notifier_ctx_t *notifier_ctx_ptr)
{
	if(notifier_ctx_ptr == NULL)
	    return -1;
  
   	notifier_t *np = NULL;
   	notifier_t *np_next = NULL;

	/* exit notify */
	notify(notifier_ctx_ptr, 0, 0);

	pthread_mutex_lock(&notifier_ctx_ptr->lock);

    np = notifier_ctx_ptr->notif;
    while (np != NULL) 
    {
		np_next = np->next;
		free(np);
		np = np_next;
    }
	notifier_ctx_ptr->notif = NULL;

	pthread_mutex_unlock(&notifier_ctx_ptr->lock);

	pthread_mutex_destroy(&notifier_ctx_ptr->lock);

	free(notifier_ctx_ptr);

	return 0;
}

/* 以下为 demo API， 放到其它文件 */

notifier_ctx_t *lzm_notifier_ctx = NULL;

/**
 * @name   lzm_register_notifier
 * @brief 
 * @param  
 * @retval 
 * @author lzm
 */
int lzm_register_notifier(notify_func func, void *arg)
{
	return add_notifier(lzm_notifier_ctx, func, arg);
}

/**
 * @name   lzm_remove_notifier
 * @brief  api
 * @param  
 * @retval 
 * @author lzm
 */
void lzm_remove_notifier(notify_func func, void *arg)
{
    remove_notifier(lzm_notifier_ctx, func, arg);
}

/**
 * @name   lzm_notify
 * @brief  api
 * @param  
 * @retval 
 * @author lzm
 */
void lzm_notify(int type, int val)
{
    notify(lzm_notifier_ctx, type, val);
}

/**
 * @name   lzm_notify_init
 * @brief  api
 * @param  
 * @retval 
 * @author lzm
 */
void lzm_notify_init(void)
{
    notify_init(lzm_notifier_ctx);
}

/**
 * @name   lzm_notify_exit
 * @brief  api
 * @param  
 * @retval 
 * @author lzm
 */
void lzm_notify_exit(void)
{
    notify_exit(lzm_notifier_ctx);
}